#define DEBUG
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Design;
using System.Drawing.Text;
using System.IO;
using System.Text;
using System.Windows.Forms;
using ICSharpCode.TextEditor.Actions;
using ICSharpCode.TextEditor.Document;
using ICSharpCode.TextEditor.Gui.CompletionWindow;
using ICSharpCode.TextEditor.Util;

namespace ICSharpCode.TextEditor
{
    [ToolboxItem(false)]
    public abstract class TextEditorControlBase : UserControl
    {
        private int updateLevel = 0;

        private bool borderVisable = false;

        private string currentFileName = null;

        protected IDocument document;

        protected Dictionary<Keys, IEditAction> editactions = new Dictionary<Keys, IEditAction>();

        private Encoding encoding;

        private Color borderColor = SystemColors.ControlLight;

        private ImageLayout _imageLayout = ImageLayout.None;

        private Image _backgroundImage = null;

        [Browsable(false)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public ITextEditorProperties TextEditorProperties
        {
            get
            {
                return document.TextEditorProperties;
            }
            set
            {
                document.TextEditorProperties = value;
                OptionsChanged();
            }
        }

        [Browsable(false)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public Encoding Encoding
        {
            get
            {
                if (encoding == null)
                {
                    return TextEditorProperties.Encoding;
                }
                return encoding;
            }
            set
            {
                encoding = value;
            }
        }

        [Browsable(false)]
        [ReadOnly(true)]
        public string FileName
        {
            get
            {
                return currentFileName;
            }
            set
            {
                if (currentFileName != value)
                {
                    currentFileName = value;
                    OnFileNameChanged(EventArgs.Empty);
                }
            }
        }

        [Browsable(false)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public IDocument Document
        {
            get
            {
                return document;
            }
            set
            {
                if (value == null)
                {
                    throw new ArgumentNullException("value");
                }
                if (document != null)
                {
                    document.DocumentChanged -= OnDocumentChanged;
                }
                document = value;
                document.UndoStack.TextEditorControl = this;
                document.DocumentChanged += OnDocumentChanged;
            }
        }

        [EditorBrowsable(EditorBrowsableState.Always)]
        [Browsable(true)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
        [Editor("System.ComponentModel.Design.MultilineStringEditor, System.Design, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", typeof(UITypeEditor))]
        public override string Text
        {
            get
            {
                return Document.TextContent;
            }
            set
            {
                Document.TextContent = value;
            }
        }

        [Browsable(false)]
        public bool IsReadOnly
        {
            get
            {
                return Document.ReadOnly;
            }
            set
            {
                Document.ReadOnly = value;
            }
        }

        public bool ComparisonState
        {
            get
            {
                return TextEditorProperties.ComparisonState;
            }
            set
            {
                document.TextEditorProperties.ComparisonState = value;
                OptionsChanged();
            }
        }

        public bool ShowGuidelines
        {
            get
            {
                return TextEditorProperties.ShowGuidelines;
            }
            set
            {
                document.TextEditorProperties.ShowGuidelines = value;
                OptionsChanged();
                ShowGuidelinesChanged();
            }
        }

        [Browsable(false)]
        public bool IsInUpdate => updateLevel > 0;

        protected override Size DefaultSize => new Size(100, 100);

        [Category("Appearance")]
        [DefaultValue(false)]
        [Description("If true spaces are shown in the textarea")]
        public bool ShowSpaces
        {
            get
            {
                return document.TextEditorProperties.ShowSpaces;
            }
            set
            {
                document.TextEditorProperties.ShowSpaces = value;
                OptionsChanged();
            }
        }

        [Category("Appearance")]
        [DefaultValue(TextRenderingHint.SystemDefault)]
        [Description("Specifies the quality of text rendering (whether to use hinting and/or anti-aliasing).")]
        public TextRenderingHint TextRenderingHint
        {
            get
            {
                return document.TextEditorProperties.TextRenderingHint;
            }
            set
            {
                document.TextEditorProperties.TextRenderingHint = value;
                OptionsChanged();
            }
        }

        [Category("Appearance")]
        [DefaultValue(false)]
        [Description("If true tabs are shown in the textarea")]
        public bool ShowTabs
        {
            get
            {
                return document.TextEditorProperties.ShowTabs;
            }
            set
            {
                document.TextEditorProperties.ShowTabs = value;
                OptionsChanged();
            }
        }

        [Category("Appearance")]
        [DefaultValue(false)]
        [Description("If true EOL markers are shown in the textarea")]
        public bool ShowEOLMarkers
        {
            get
            {
                return document.TextEditorProperties.ShowEOLMarker;
            }
            set
            {
                document.TextEditorProperties.ShowEOLMarker = value;
                OptionsChanged();
            }
        }

        [Category("Appearance")]
        [DefaultValue(false)]
        [Description("If true the horizontal ruler is shown in the textarea")]
        public bool ShowHRuler
        {
            get
            {
                return document.TextEditorProperties.ShowHorizontalRuler;
            }
            set
            {
                document.TextEditorProperties.ShowHorizontalRuler = value;
                OptionsChanged();
            }
        }

        [Category("Appearance")]
        [DefaultValue(true)]
        [Description("If true the vertical ruler is shown in the textarea")]
        public bool ShowVRuler
        {
            get
            {
                return document.TextEditorProperties.ShowVerticalRuler;
            }
            set
            {
                document.TextEditorProperties.ShowVerticalRuler = value;
                OptionsChanged();
            }
        }

        [Category("Appearance")]
        [DefaultValue(80)]
        [Description("The row in which the vertical ruler is displayed")]
        public int VRulerRow
        {
            get
            {
                return document.TextEditorProperties.VerticalRulerRow;
            }
            set
            {
                document.TextEditorProperties.VerticalRulerRow = value;
                OptionsChanged();
            }
        }

        [Category("Appearance")]
        [DefaultValue(true)]
        [Description("If true line numbers are shown in the textarea")]
        public bool ShowLineNumbers
        {
            get
            {
                return document.TextEditorProperties.ShowLineNumbers;
            }
            set
            {
                document.TextEditorProperties.ShowLineNumbers = value;
                OptionsChanged();
            }
        }

        [Category("Appearance")]
        [DefaultValue(false)]
        [Description("If true invalid lines are marked in the textarea")]
        public bool ShowInvalidLines
        {
            get
            {
                return document.TextEditorProperties.ShowInvalidLines;
            }
            set
            {
                document.TextEditorProperties.ShowInvalidLines = value;
                OptionsChanged();
            }
        }

        [Category("Appearance")]
        [DefaultValue(true)]
        [Description("If true folding is enabled in the textarea")]
        public bool EnableFolding
        {
            get
            {
                return document.TextEditorProperties.EnableFolding;
            }
            set
            {
                document.TextEditorProperties.EnableFolding = value;
                OptionsChanged();
            }
        }

        [Category("Appearance")]
        [DefaultValue(true)]
        [Description("If true matching brackets are highlighted")]
        public bool ShowMatchingBracket
        {
            get
            {
                return document.TextEditorProperties.ShowMatchingBracket;
            }
            set
            {
                document.TextEditorProperties.ShowMatchingBracket = value;
                OptionsChanged();
            }
        }

        [Category("Appearance")]
        [Description("This Border Color")]
        public Color BorderColor
        {
            get
            {
                return borderColor;
            }
            set
            {
                borderColor = value;
                BorderVisableChanged();
                Invalidate();
            }
        }

        [Category("Appearance")]
        [DefaultValue(false)]
        [Description("If true the icon bar is displayed")]
        public bool IsIconBarVisible
        {
            get
            {
                return document.TextEditorProperties.IsIconBarVisible;
            }
            set
            {
                document.TextEditorProperties.IsIconBarVisible = value;
                OptionsChanged();
            }
        }

        [Category("Appearance")]
        [DefaultValue(false)]
        [Description("Click this Mark is remove")]
        public bool BookMarkEnableToggle
        {
            get
            {
                return document.TextEditorProperties.BookMarkEnableToggle;
            }
            set
            {
                document.TextEditorProperties.BookMarkEnableToggle = value;
                OptionsChanged();
            }
        }

        [Category("Appearance")]
        [DefaultValue(4)]
        [Description("The width in spaces of a tab character")]
        public int TabIndent
        {
            get
            {
                return document.TextEditorProperties.TabIndent;
            }
            set
            {
                document.TextEditorProperties.TabIndent = value;
                OptionsChanged();
            }
        }

        [Category("Appearance")]
        [DefaultValue(0)]
        [Description("The Fold Mark Style 0 默认 Visual Studio样式 1: PyCharm 样式  2: idea 3: eclipse ")]
        public int FoldMarkStyle
        {
            get
            {
                return document.TextEditorProperties.FoldMarkStyle;
            }
            set
            {
                document.TextEditorProperties.FoldMarkStyle = value;
                OptionsChanged();
            }
        }

        [Category("Appearance")]
        [DefaultValue(LineViewerStyle.None)]
        [Description("The line viewer style")]
        public LineViewerStyle LineViewerStyle
        {
            get
            {
                return document.TextEditorProperties.LineViewerStyle;
            }
            set
            {
                document.TextEditorProperties.LineViewerStyle = value;
                OptionsChanged();
            }
        }

        [Category("Behavior")]
        [DefaultValue(IndentStyle.Smart)]
        [Description("The indent style")]
        public IndentStyle IndentStyle
        {
            get
            {
                return document.TextEditorProperties.IndentStyle;
            }
            set
            {
                document.TextEditorProperties.IndentStyle = value;
                OptionsChanged();
            }
        }

        [Category("Behavior")]
        [DefaultValue(false)]
        [Description("The Border Visable")]
        public bool BorderVisable
        {
            get
            {
                return borderVisable;
            }
            set
            {
                borderVisable = value;
                BorderVisableChanged();
                Invalidate();
            }
        }

        [Category("Behavior")]
        [DefaultValue(false)]
        [Description("Converts tabs to spaces while typing")]
        public bool ConvertTabsToSpaces
        {
            get
            {
                return document.TextEditorProperties.ConvertTabsToSpaces;
            }
            set
            {
                document.TextEditorProperties.ConvertTabsToSpaces = value;
                OptionsChanged();
            }
        }

        [Category("Behavior")]
        [DefaultValue(false)]
        [Description("Hide the mouse cursor while typing")]
        public bool HideMouseCursor
        {
            get
            {
                return document.TextEditorProperties.HideMouseCursor;
            }
            set
            {
                document.TextEditorProperties.HideMouseCursor = value;
                OptionsChanged();
            }
        }

        [Category("Behavior")]
        [DefaultValue(false)]
        [Description("Allows the caret to be placed beyond the end of line")]
        public bool AllowCaretBeyondEOL
        {
            get
            {
                return document.TextEditorProperties.AllowCaretBeyondEOL;
            }
            set
            {
                document.TextEditorProperties.AllowCaretBeyondEOL = value;
                OptionsChanged();
            }
        }

        [Category("Behavior")]
        [DefaultValue(BracketMatchingStyle.After)]
        [Description("Specifies if the bracket matching should match the bracket before or after the caret.")]
        public BracketMatchingStyle BracketMatchingStyle
        {
            get
            {
                return document.TextEditorProperties.BracketMatchingStyle;
            }
            set
            {
                document.TextEditorProperties.BracketMatchingStyle = value;
                OptionsChanged();
            }
        }

        [Browsable(true)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
        [Description("The base font of the text area. No bold or italic fonts can be used because bold/italic is reserved for highlighting purposes.")]
        public override Font Font
        {
            get
            {
                return document.TextEditorProperties.Font;
            }
            set
            {
                document.TextEditorProperties.Font = value;
                OptionsChanged();
            }
        }

        public ImageLayout ImageLayout
        {
            get
            {
                return _imageLayout;
            }
            set
            {
                _imageLayout = value;
                Invalidate();
            }
        }

        [Browsable(true)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
        [Description("TextEditorControl BackgroundImage")]
        public new Image BackgroundImage
        {
            get
            {
                return _backgroundImage;
            }
            set
            {
                _backgroundImage = value;
            }
        }

        public abstract TextAreaControl ActiveTextAreaControl { get; }

        public event EventHandler TextAreaTextChanged;

        public event ShowPromptEventHandler ShowPrompt;

        public event EventHandler FileNameChanged;

        private void OnDocumentChanged(object sender, EventArgs e)
        {
            OnTextChanged(e);
        }

        public virtual void OnTextAreaTextChanged(EventArgs e)
        {
            if (TextAreaTextChanged != null)
            {
                TextAreaTextChanged(this, e);
            }
        }

        public virtual ICompletionData[] DoShowPromptBox(ShowPromptEventArgs e)
        {
            if (ShowPrompt != null)
            {
                return ShowPrompt(this, e);
            }
            return null;
        }

        private static Font ParseFont(string font)
        {
            string[] array = font.Split(',', '=');
            return new Font(array[1], float.Parse(array[3]));
        }

        protected TextEditorControlBase()
        {
            GenerateThemeMaps();
            GenerateDefaultActions();
            GenerateDefaultEvents();
        }

        public bool IsEditAction(Keys keyData)
        {
            return editactions.ContainsKey(keyData);
        }

        internal IEditAction GetEditAction(Keys keyData)
        {
            if (!IsEditAction(keyData))
            {
                return null;
            }
            return editactions[keyData];
        }

        private void GenerateThemeMaps()
        {
            ThemesMapManager.GenerateThemeMap("Default");
        }

        private void GenerateDefaultActions()
        {
            editactions[Keys.Left] = new CaretLeft();
            editactions[Keys.Left | Keys.Shift] = new ShiftCaretLeft();
            editactions[Keys.Left | Keys.Control] = new WordLeft();
            editactions[Keys.Left | Keys.Shift | Keys.Control] = new ShiftWordLeft();
            editactions[Keys.Right] = new CaretRight();
            editactions[Keys.Right | Keys.Shift] = new ShiftCaretRight();
            editactions[Keys.Right | Keys.Control] = new WordRight();
            editactions[Keys.Right | Keys.Shift | Keys.Control] = new ShiftWordRight();
            editactions[Keys.Up] = new CaretUp();
            editactions[Keys.Up | Keys.Shift] = new ShiftCaretUp();
            editactions[Keys.Up | Keys.Control] = new ScrollLineUp();
            editactions[Keys.Down] = new CaretDown();
            editactions[Keys.Down | Keys.Shift] = new ShiftCaretDown();
            editactions[Keys.Down | Keys.Control] = new ScrollLineDown();
            editactions[Keys.Insert] = new ToggleEditMode();
            editactions[Keys.Insert | Keys.Control] = new Copy();
            editactions[Keys.Insert | Keys.Shift] = new Paste();
            editactions[Keys.Delete] = new Delete();
            editactions[Keys.Delete | Keys.Shift] = new Cut();
            editactions[Keys.Home] = new Home();
            editactions[Keys.Home | Keys.Shift] = new ShiftHome();
            editactions[Keys.Home | Keys.Control] = new MoveToStart();
            editactions[Keys.Home | Keys.Shift | Keys.Control] = new ShiftMoveToStart();
            editactions[Keys.End] = new End();
            editactions[Keys.End | Keys.Shift] = new ShiftEnd();
            editactions[Keys.End | Keys.Control] = new MoveToEnd();
            editactions[Keys.End | Keys.Shift | Keys.Control] = new ShiftMoveToEnd();
            editactions[Keys.Prior] = new MovePageUp();
            editactions[Keys.Prior | Keys.Shift] = new ShiftMovePageUp();
            editactions[Keys.Next] = new MovePageDown();
            editactions[Keys.Next | Keys.Shift] = new ShiftMovePageDown();
            editactions[Keys.Return] = new Return();
            editactions[Keys.Tab] = new Tab();
            editactions[Keys.Tab | Keys.Shift] = new ShiftTab();
            editactions[Keys.Back] = new Backspace();
            editactions[Keys.Back | Keys.Shift] = new Backspace();
            editactions[Keys.X | Keys.Control] = new Cut();
            editactions[Keys.C | Keys.Control] = new Copy();
            editactions[Keys.V | Keys.Control] = new Paste();
            editactions[Keys.A | Keys.Control] = new SelectWholeDocument();
            editactions[Keys.Escape] = new ClearAllSelections();
            editactions[Keys.Divide | Keys.Control] = new ToggleComment();
            editactions[Keys.OemQuestion | Keys.Control] = new ToggleComment();
            editactions[Keys.Back | Keys.Alt] = new ICSharpCode.TextEditor.Actions.Undo();
            editactions[Keys.Z | Keys.Control] = new ICSharpCode.TextEditor.Actions.Undo();
            editactions[Keys.Y | Keys.Control] = new Redo();
            editactions[Keys.Delete | Keys.Control] = new DeleteWord();
            editactions[Keys.Back | Keys.Control] = new WordBackspace();
            editactions[Keys.D | Keys.Control] = new DeleteLine();
            editactions[Keys.D | Keys.Shift | Keys.Control] = new DeleteToLineEnd();
            editactions[Keys.B | Keys.Control] = new GotoMatchingBrace();
            editactions[Keys.U | Keys.Control] = new ToLowerCase();
            editactions[Keys.U | Keys.Control | Keys.Shift] = new ToUpperCase();
            editactions[Keys.F | Keys.Control] = new QueryText();
            editactions[Keys.S | Keys.Control] = new SaveContent();
        }

        private void GenerateDefaultEvents()
        {
            HighlightingManager.Manager.ReloadSyntaxHighlighting += OnReloadHighlighting;
        }

        public virtual void BeginUpdate()
        {
            checked
            {
                updateLevel++;
            }
        }

        public virtual void EndUpdate()
        {
            //Debug.Assert(updateLevel > 0);
            updateLevel = Math.Max(0, checked(updateLevel - 1));
        }

        public new virtual void Invalidate()
        {
            ActiveTextAreaControl.Invalidate();
        }

        public void LoadFile(string fileName)
        {
            LoadFile(fileName, autoLoadHighlighting: true, autodetectEncoding: true);
        }

        public void LoadFile(string fileName, bool autoLoadHighlighting, bool autodetectEncoding)
        {
            using (FileStream stream = new FileStream(fileName, FileMode.Open, FileAccess.Read))
            {
                LoadFile(fileName, stream, autoLoadHighlighting, autodetectEncoding);
            }
        }

        public void LoadFile(string fileName, Stream stream, bool autoLoadHighlighting, bool autodetectEncoding)
        {
            if (stream == null)
            {
                throw new ArgumentNullException("stream");
            }
            BeginUpdate();
            document.TextContent = string.Empty;
            document.UndoStack.ClearAll();
            document.BookmarkManager.Clear();
            if (autoLoadHighlighting)
            {
                try
                {
                    document.HighlightingStrategy = HighlightingStrategyFactory.CreateHighlightingStrategyForFile(fileName);
                }
                catch (HighlightingDefinitionInvalidException ex)
                {
                    MessageBox.Show(ex.ToString(), "Error", MessageBoxButtons.OK, MessageBoxIcon.Hand);
                }
            }
            if (autodetectEncoding)
            {
                Encoding encoding = Encoding;
                Document.TextContent = FileReader.ReadFileContent(stream, ref encoding);
                Encoding = encoding;
            }
            else
            {
                using (StreamReader streamReader = new StreamReader(fileName, Encoding))
                {
                    Document.TextContent = streamReader.ReadToEnd();
                }
            }
            FileName = fileName;
            Document.UpdateQueue.Clear();
            EndUpdate();
            OptionsChanged();
            Refresh();
        }

        public bool CanSaveWithCurrentEncoding()
        {
            if (encoding == null || FileReader.IsUnicode(encoding))
            {
                return true;
            }
            string textContent = document.TextContent;
            return encoding.GetString(encoding.GetBytes(textContent)) == textContent;
        }

        public void SaveFile(string fileName)
        {
            using (FileStream stream = new FileStream(fileName, FileMode.Create, FileAccess.Write))
            {
                SaveFile(stream);
            }
            FileName = fileName;
        }

        public void SaveFile(Stream stream)
        {
            StreamWriter streamWriter = new StreamWriter(stream, Encoding ?? Encoding.UTF8);
            foreach (LineSegment item in Document.LineSegmentCollection)
            {
                streamWriter.Write(Document.GetText(item.Offset, item.Length));
                if (item.DelimiterLength > 0)
                {
                    char charAt = Document.GetCharAt(checked(item.Offset + item.Length));
                    if (charAt != '\n' && charAt != '\r')
                    {
                        throw new InvalidOperationException("The document cannot be saved because it is corrupted.");
                    }
                    streamWriter.Write(document.TextEditorProperties.LineTerminator);
                }
            }
            streamWriter.Flush();
            document.ResetLinesMark();
        }

        public void TextChangeCommit()
        {
            document.ResetLinesMark();
            if (ActiveTextAreaControl != null && ActiveTextAreaControl.TextArea != null)
            {
                ActiveTextAreaControl.TextArea.Invalidate();
            }
        }

        public abstract void OptionsChanged();

        public abstract void ShowGuidelinesChanged();

        public abstract void BorderVisableChanged();

        public virtual string GetRangeDescription(int selectedItem, int itemCount)
        {
            StringBuilder stringBuilder = new StringBuilder(selectedItem.ToString());
            stringBuilder.Append(" from ");
            stringBuilder.Append(itemCount.ToString());
            return stringBuilder.ToString();
        }

        public override void Refresh()
        {
            if (!IsInUpdate)
            {
                base.Refresh();
            }
        }

        protected override void OnPaint(PaintEventArgs e)
        {
            base.OnPaint(e);
            if (borderVisable)
            {
                ControlPaint.DrawBorder(e.Graphics, base.ClientRectangle, borderColor, 1, ButtonBorderStyle.Solid, borderColor, 1, ButtonBorderStyle.Solid, borderColor, 1, ButtonBorderStyle.Solid, borderColor, 1, ButtonBorderStyle.Solid);
            }
        }

        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                HighlightingManager.Manager.ReloadSyntaxHighlighting -= OnReloadHighlighting;
                document.HighlightingStrategy = null;
                document.UndoStack.TextEditorControl = null;
            }
            base.Dispose(disposing);
        }

        protected virtual void OnFileNameChanged(EventArgs e)
        {
            if (FileNameChanged != null)
            {
                FileNameChanged(this, e);
            }
        }

        protected virtual void OnReloadHighlighting(object sender, EventArgs e)
        {
            if (Document.HighlightingStrategy != null)
            {
                try
                {
                    Document.HighlightingStrategy = HighlightingStrategyFactory.CreateHighlightingStrategy(Document.HighlightingStrategy.Name);
                }
                catch (HighlightingDefinitionInvalidException ex)
                {
                    MessageBox.Show(ex.ToString(), "Error", MessageBoxButtons.OK, MessageBoxIcon.Hand);
                }
                OptionsChanged();
            }
        }
    }
}
